package com.lizhen.core.function.reflect;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Properties;

/**
 * @author lizhen
 */
public class Reflect {

    private static final String FILE_PATH = "1.8\\api\\main\\resource\\com\\lizhen\\core\\function\\reflect\\Reflect\\ReflectObj.txt";
    private static final String INNER_CLASS = "com.lizhen.core.function.reflect.Reflect$ReflectObj";

    public static String getValue(String key) {
        FileReader fileReader = null;
        Properties properties = null;
        try {
            fileReader = new FileReader(FILE_PATH);
            properties = new Properties();
            properties.load(fileReader);
            fileReader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return (properties != null) ? properties.getProperty(key) : null;
        }
    }

    public static void main(String[] args) {
        Reflect reflect = new Reflect();
        reflect.reflectClassName();
        reflect.reflectMethodOverTypeCheck();
        reflect.reflectNoStaticMethod();
        reflect.reflectAccessControlMethod();
        reflect.reflectAccessControlVariable();
        reflect.ReflectAccessControlConstructor();
        reflect.reflectStaticMethod();
        reflect.reflectClassloader();
    }

    /**
     * @method reflectClassName
     * @author lizhen
     */
    private void reflectClassName() {
        ReflectObj reflectObj = new ReflectObj();
        Class<? extends ReflectObj> stuClass = reflectObj.getClass();
        Class<ReflectObj> stuClass2 = ReflectObj.class;
        try {
            Class<?> stuClass3 = Class.forName(INNER_CLASS);

            System.out.println("private void TestRefClsObj()");
            System.out.println(stuClass.getName());
            System.out.println(stuClass2.getName());
            System.out.println(stuClass3.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * @method reflectMethodOverTypeCheck
     * @author lizhen
     */
    private void reflectMethodOverTypeCheck() {
        ArrayList<String> strList = new ArrayList<>();
        strList.add("aaa");
        strList.add("bbb");
//        类型检查出错
//        strList.add(100);
        Class<?> clazz = strList.getClass();
        try {
            Method method = clazz.getMethod("add", Object.class);
            method.invoke(strList, 100);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        System.out.println("private void reflectMethodOverTypeCheck()");
        for (Object object : strList) {
            System.out.println(object);
        }
    }

    /**
     * @method reflectNoStaticMethod
     * @description About Reflect method object by configuration
     * @author lizhen
     */
    private void reflectNoStaticMethod() {
        try {
            Class<?> clazz = Class.forName(getValue("className"));
            Method method = clazz.getMethod(getValue("methodName"));

            System.out.println("private void reflectNoStaticMethod()");

            method.invoke(clazz.getConstructor().newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * @method reflectAccessControlMethod
     * @description About Reflect member method object
     * @author lizhen
     */
    private void reflectAccessControlMethod() {
        try {
            Class<?> clazz = Class.forName(INNER_CLASS);
            Method[] methods = clazz.getMethods();
            Method[] declaredMethods = clazz.getDeclaredMethods();

            System.out.println("private void reflectAccessControlMethod()");
            for (Method _method : methods) {
                System.out.println(_method);
            }
            for (Method _method : declaredMethods) {
                System.out.println(_method);
            }

            Method method = clazz.getMethod("show1", String.class);
            Method declaredMethod = clazz.getDeclaredMethod("show4", int.class);
            declaredMethod.setAccessible(true);

            System.out.println(method);
            System.out.println(declaredMethod);

            Object instance = clazz.getConstructor().newInstance();
            method.invoke(instance, "111");
            Object declaredInstance = declaredMethod.invoke(instance, 20);

            System.out.println(declaredInstance);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * @method reflectAccessControlVariable
     * @description About Reflect member variable object
     * @author lizhen
     */
    private void reflectAccessControlVariable() {
        try {
            Class<?> clazz = Class.forName(INNER_CLASS);
            Field[] fields = clazz.getFields();
            Field[] declaredFields = clazz.getDeclaredFields();

            System.out.println("private void reflectAccessControlVariable()");
            for (Field field : fields) {
                System.out.println(field);
            }
            for (Field field : declaredFields) {
                System.out.println(field);
            }

            Field nameField = clazz.getField("name");
            Field phoneNumDeclaredField = clazz.getDeclaredField("phoneNum");
            phoneNumDeclaredField.setAccessible(true);

            System.out.println(nameField);
            System.out.println(phoneNumDeclaredField);

            Object instance = clazz.getConstructor().newInstance();
            nameField.set(instance, "小明");
            ReflectObj reflectObj = (ReflectObj) instance;
            phoneNumDeclaredField.set(instance, "18888888888");

            System.out.println(reflectObj.name);
            System.out.println(phoneNumDeclaredField.get(reflectObj));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    /**
     * @method ReflectAccessControlConstructor
     * @description About Reflect constructor object
     * @author lizhen
     */
    private void ReflectAccessControlConstructor() {
        try {
            Class<?> clazz = Class.forName(INNER_CLASS);
            Constructor<?>[] constructors = clazz.getConstructors();
            Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();

            System.out.println("private void ReflectAccessControlConstructor()");
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor);
            }
            for (Constructor<?> constructor : declaredConstructors) {
                System.out.println(constructor);
            }

            Constructor<?> constructor = clazz.getConstructor();
            Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(int.class);
            declaredConstructor.setAccessible(true);

            System.out.println(constructor);
            System.out.println(declaredConstructor);

            Object instance = constructor.newInstance();
            ReflectObj reflectObj = (ReflectObj) instance;
            Object declaredInstance = declaredConstructor.newInstance(12345);

            System.out.println(instance);
            System.out.println(reflectObj);
            System.out.println(declaredInstance);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * @method reflectStaticMethod
     * @author lizhen
     */
    private void reflectStaticMethod() {
        try {
            Class<?> clazz = Class.forName(INNER_CLASS);
            Method mainMethod = clazz.getMethod("main", String[].class);

            System.out.println("private void TestRefMainmethodObj()");
//            传参出错
//            mainMethod.invoke(null, new String[]{"a", "b", "c"});
            System.out.println(new String[]{"a", "b", "c"}.length);
            System.out.println(new Object[]{new String[]{"a", "b", "c"}}.length);
            mainMethod.invoke(null, (Object) new String[]{"a", "b", "c"});
            mainMethod.invoke(null, new Object[]{new String[]{"a", "b", "c"}});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void reflectClassloader() {
        ClassLoader reflect = Reflect.class.getClassLoader();
        ClassLoader reflectParent = reflect.getParent();
        ClassLoader reflectParentParent = reflectParent.getParent();

        System.out.println(reflect);
        System.out.println(reflectParent);
        System.out.println(reflectParentParent);
        System.out.println(Thread.currentThread().getContextClassLoader());
    }

    static class ReflectObj {

        public String name;
        protected int age;
        char sex;
        private String phoneNum;

        public ReflectObj() {
            System.out.println("public ReflectObj()");
        }

        protected ReflectObj(boolean n) {
            System.out.println("protected ReflectObj(boolean n) n=" + n);
        }

        ReflectObj(String str) {
            System.out.println("ReflectObj(String str) s=" + str);
        }

        private ReflectObj(int age) {
            System.out.println("private ReflectObj(int age) age=" + age);
        }

        public ReflectObj(char name) {
            System.out.println("public ReflectObj(char name) name=" + name);
        }

        public ReflectObj(String name, int age) {
            System.out.println("public ReflectObj(String name, int age) name=" + name + " age=" + age);
        }

        public static void main(String[] args) {
            System.out.println("public static void main(String[] args)");
        }

        public void show() {
            System.out.println("public void show()");
        }

        public void show1(String s) {
            System.out.println("public void show1(String s) s=" + s);
        }

        protected void show2() {
            System.out.println("protected void show2()");
        }

        void show3() {
            System.out.println("void show3()");
        }

        private String show4(int age) {
            System.out.println("private String show4(int age) age=" + age);

            return "abcd";
        }

        @Override
        public String toString() {
            return "Student [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]";
        }
    }
}